{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### CNN for CIFAR10\n",
    "\n",
    "CNN model that can be used for classification tasks. \n",
    "\n",
    "In this demo, we will train a 3-layer CNN on the CIFAR10 dataset. We will show 2 implementations of the CNN model. First is using PyTorch's  built-in `nn.Conv2d` API. Second, is we will use tensor level convolution. \n",
    "\n",
    "Let us first import the required modules."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torchvision\n",
    "import math\n",
    "import time\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "from torch import nn\n",
    "from torch.optim import Adam\n",
    "from torch.optim.lr_scheduler import CosineAnnealingLR\n",
    "from matplotlib import image\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### CNN using PyTorch `nn.Conv2D`\n",
    "\n",
    "In this example, we use `nn.Conv2D` to create a 3-layer CNN model. Note the following:\n",
    "1. The first layer number of input features is equal to the number of input RGB channels (3).\n",
    "2. The output of the first layer is equal to the number of input features of the second layer.\n",
    "3. The same matching for the second and third (last) layer.\n",
    "4. We use `nn.MaxPool2d` to reduce the output feature map size.\n",
    "5. At the same, we increase the number of feature maps after every layer.\n",
    "6. We use `nn.ReLU` to activate the output of the layer.\n",
    "7. For the last linear layer `nn.Linear`, the number of input features has to be supplied manually. Below in comment is a simple script that can be used to calculate the number of input features.\n",
    "\n",
    "Ideas for experimentation:\n",
    "1. Try other kernel sizes\n",
    "2. Try deeper models\n",
    "3. Try different activation functions\n",
    "4. Try applying skip connections\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SimpleCNN(nn.Module):\n",
    "    def __init__(self, n_features=3, kernel_size=3, n_filters=32, num_classes=10, conv2d=nn.Conv2d):\n",
    "        super().__init__()\n",
    "        self.conv1 = conv2d(n_features, n_filters, kernel_size=kernel_size)\n",
    "        self.conv2 = conv2d(n_filters, n_filters*2, kernel_size=kernel_size)\n",
    "        self.conv3 = conv2d(n_filters*2, n_filters*4, kernel_size=kernel_size)\n",
    "        \n",
    "        self.fc1 = nn.Linear(2048, num_classes)\n",
    "\n",
    "    def forward(self, x):\n",
    "        y = nn.ReLU()(self.conv1(x))\n",
    "        y = nn.MaxPool2d(kernel_size=2)(y)\n",
    "        y = nn.ReLU()(self.conv2(y))\n",
    "        y = nn.MaxPool2d(kernel_size=2)(y)\n",
    "        y = nn.ReLU()(self.conv3(y))\n",
    "        y = y.contiguous().view(y.size(0), -1)\n",
    "\n",
    "        y = self.fc1(y)\n",
    "        return y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Dataloaders\n",
    "\n",
    "Create train and test data loaders. We use the `torchvision` module to download the CIFAR10 dataset. We also apply some data augmentation to the training dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Files already downloaded and verified\n",
      "Files already downloaded and verified\n"
     ]
    }
   ],
   "source": [
    "batch_size = 64\n",
    "# load the CIFAR10 dataset\n",
    "transform_train = torchvision.transforms.Compose([\n",
    "    torchvision.transforms.RandomCrop(32, padding=4),\n",
    "    torchvision.transforms.RandomHorizontalFlip(),\n",
    "    torchvision.transforms.ToTensor(),\n",
    "    torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n",
    "])\n",
    "transform_test = torchvision.transforms.Compose([\n",
    "    torchvision.transforms.ToTensor(),\n",
    "    torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n",
    "])\n",
    "trainset = torchvision.datasets.CIFAR10(root='~/data', train=True, download=True, transform=transform_train)\n",
    "train_loader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True, num_workers=2)\n",
    "testset = torchvision.datasets.CIFAR10(root='~/data', train=False, download=True, transform=transform_test)\n",
    "test_loader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Train and Test Functions\n",
    "\n",
    "We define the train and test functions. Note that we use the `nn.CrossEntropyLoss` loss function. We also use the `torch.optim.Adam` optimizer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define the training function\n",
    "def train(model, train_loader, optimizer, criterion,  device):\n",
    "    model.train()\n",
    "    train_loss = 0\n",
    "    correct = 0\n",
    "    total = 0\n",
    "    for batch_idx, (inputs, targets) in enumerate(train_loader):\n",
    "        inputs, targets = inputs.to(device), targets.to(device)\n",
    "        optimizer.zero_grad()\n",
    "        outputs = model(inputs)\n",
    "        loss = criterion(outputs, targets)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        train_loss += loss.item()\n",
    "        _, predicted = outputs.max(1)\n",
    "        total += targets.size(0)\n",
    "        correct += predicted.eq(targets).sum().item()\n",
    "\n",
    "    return train_loss/(batch_idx+1), 100.*correct/total\n",
    "\n",
    "# define the test function\n",
    "def test(model, test_loader, criterion, device):\n",
    "    model.eval()\n",
    "    test_loss = 0\n",
    "    correct = 0\n",
    "    total = 0\n",
    "    with torch.no_grad():\n",
    "        for batch_idx, (inputs, targets) in enumerate(test_loader):\n",
    "            inputs, targets = inputs.to(device), targets.to(device)\n",
    "            outputs = model(inputs)\n",
    "            loss = criterion(outputs, targets)\n",
    "\n",
    "            test_loss += loss.item()\n",
    "            _, predicted = outputs.max(1)\n",
    "            total += targets.size(0)\n",
    "            correct += predicted.eq(targets).sum().item()\n",
    "\n",
    "    return test_loss/(batch_idx+1), 100.*correct/total"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create the `main()` function\n",
    "\n",
    "We create the `main()` function. The `main()` function is the entry point of the program. We also define the hyperparameters of the model. We also define the number of epochs to train the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of model parameters: 0.11 million\n",
      "Running on: cuda\n",
      "Epoch: 1, Train Loss: 1.5890, Train Acc: 41.47%, Test Loss: 1.2760, Test Acc: 53.57%, LR: 0.000976, Time: 11.57s\n",
      "Epoch: 2, Train Loss: 1.2822, Train Acc: 53.92%, Test Loss: 1.0892, Test Acc: 61.30%, LR: 0.000905, Time: 11.48s\n",
      "Epoch: 3, Train Loss: 1.1171, Train Acc: 60.42%, Test Loss: 0.9790, Test Acc: 65.63%, LR: 0.000794, Time: 11.57s\n",
      "Epoch: 4, Train Loss: 1.0223, Train Acc: 64.02%, Test Loss: 0.8940, Test Acc: 68.52%, LR: 0.000655, Time: 11.56s\n",
      "Epoch: 5, Train Loss: 0.9539, Train Acc: 66.55%, Test Loss: 0.8403, Test Acc: 70.50%, LR: 0.000500, Time: 11.48s\n",
      "Epoch: 6, Train Loss: 0.8989, Train Acc: 68.66%, Test Loss: 0.7856, Test Acc: 72.29%, LR: 0.000345, Time: 11.48s\n",
      "Epoch: 7, Train Loss: 0.8546, Train Acc: 70.27%, Test Loss: 0.8091, Test Acc: 72.32%, LR: 0.000206, Time: 11.61s\n",
      "Epoch: 8, Train Loss: 0.8228, Train Acc: 71.28%, Test Loss: 0.7669, Test Acc: 73.15%, LR: 0.000095, Time: 11.56s\n",
      "Epoch: 9, Train Loss: 0.7988, Train Acc: 72.21%, Test Loss: 0.7427, Test Acc: 73.50%, LR: 0.000024, Time: 11.50s\n",
      "Epoch: 10, Train Loss: 0.7889, Train Acc: 72.66%, Test Loss: 0.7311, Test Acc: 74.15%, LR: 0.000000, Time: 11.53s\n"
     ]
    },
    {
     "data": {
      "image/png": "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mptL5OnpSadOnViwYEHZNZvNxoIFC+jevbuJyVyX3W5nxIgRzJgxg99++41GjRqZHcmlXXvttWzevJmEhISyR+fOnRk8eDAJCQkqNjWsZ8+eZ2yNsGvXLmJjY01K5Nry8vKwWsv/KnZzc8Nms5mUyDlo5KYKjBo1iqFDh9K5c2e6du3K2LFjyc3N5b777jM7mksaPnw4U6ZM4YcffsDf35/U1FQAAgMD8fHxMTmd6/H39z9jvZOfnx+hoaFaB2WCJ598kh49evD6669z5513snr1aj7++GM+/vhjs6O5pL59+/Laa68RExND69at2bBhA++88w7333+/2dEcmm4FryLjxo3jrbfeIjU1lfbt2/Pee+8RHx9vdiyXZLFYznr9iy++YNiwYTUbRs7qqquu0q3gJvrpp5947rnn2L17N40aNWLUqFE8+OCDZsdySdnZ2Tz//PPMmDGD9PR0oqKiGDhwIC+88AKenp5mx3NYKjciIiLiVLTmRkRERJyKyo2IiIg4FZUbERERcSoqNyIiIuJUVG5ERETEqajciIiIiFNRuRERERGnonIjIiIiTkXlRkQEY2frmTNnmh1DRKqAyo2ImG7YsGFYLJYzHr179zY7mog4IB2cKSK1Qu/evfniiy/KXfPy8jIpjYg4Mo3ciEit4OXlRURERLlHcHAwYEwZTZgwgT59+uDj40Pjxo359ttvy3385s2bueaaa/Dx8SE0NJSHHnqInJyccq/5/PPPad26NV5eXkRGRjJixIhy7z9y5Ai33norvr6+NG3alFmzZlXvNy0i1ULlRkQcwvPPP89tt93Gxo0bGTx4MHfffTfbt28HIDc3lxtuuIHg4GDWrFnDN998w/z588uVlwkTJjB8+HAeeughNm/ezKxZs2jSpEm5rzF69GjuvPNONm3axI033sjgwYPJyMio0e9TRKqAXUTEZEOHDrW7ubnZ/fz8yj1ee+01u91utwP2hx9+uNzHxMfH2x955BG73W63f/zxx/bg4GB7Tk5O2ft//vlnu9Vqtaemptrtdrs9KirK/s9//vOcGQD7v/71r7LnOTk5dsD+yy+/VNn3KSI1Q2tuRKRWuPrqq5kwYUK5ayEhIWVvd+/evdz7unfvTkJCAgDbt28nLi4OPz+/svf37NkTm83Gzp07sVgsJCcnc+2111aYoV27dmVv+/n5ERAQQHp6emW/JRExicqNiNQKfn5+Z0wTVRUfH58Lep2Hh0e55xaLBZvNVh2RRKQaac2NiDiElStXnvG8ZcuWALRs2ZKNGzeSm5tb9v5ly5ZhtVpp3rw5/v7+NGzYkAULFtRoZhExh0ZuRKRWKCgoIDU1tdw1d3d36tatC8A333xD586dufzyy/nqq69YvXo1n332GQCDBw/mxRdfZOjQobz00kscPnyYxx57jHvvvZfw8HAAXnrpJR5++GHCwsLo06cP2dnZLFu2jMcee6xmv1ERqXYqNyJSK8yZM4fIyMhy15o3b86OHTsA406mqVOn8uijjxIZGcnXX39Nq1atAPD19WXu3Lk8/vjjdOnSBV9fX2677Tbeeeedss81dOhQTpw4wX//+1+eeuop6taty+23315z36CI1BiL3W63mx1CRKQiFouFGTNm0L9/f7OjiIgD0JobERERcSoqNyIiIuJUtOZGRGo9zZ6LyMXQyI2IiIg4FZUbERERcSoqNyIiIuJUVG5ERETEqajciIiIiFNRuRERERGnonIjIiIiTkXlRkRERJzK/wNUp3BqLo//UAAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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66+zZcsWnn32WZ577jlee+01s0tzW7r9ugK6detG165def311wFwOBzEx8dz11138dBDD5lcnWc7fPgwUVFRLF68mN69e5tdjsfKzc2lU6dOvPnmmzz11FN06NCBV155xeyyPNJDDz3E0qVL+fnnn80uRYArr7yS6OhoZsyYUdo2fPhwAgIC+Oijj0yszH2pR6acCgsLWb16Nf369Stt8/Lyol+/fixbtszEygQgKysLgHr16plciWcbP348Q4YMKfPvRMwxd+5cunTpwogRI4iKiqJjx4688847ZpflsS6++GIWLlzI9u3bAVi/fj2//PILgwYNMrky9+VSWxS4gyNHjmC324mOji7THh0dzdatW02qSsDZM3bPPffQs2fPMzYklZqTnJzMmjVrWLlypdmlCLB7926mT5/OpEmTePjhh1m5ciV33303fn5+jB071uzyPM5DDz1EdnY2LVq0wNvbG7vdzj//+U/GjBljdmluS0FGao3x48ezadMmfvnlF7NL8VipqalMmDCB+fPn4+/vb3Y5gjPgd+nShaeffhqAjh07smnTJv79738ryJjg008/5eOPP2bWrFm0bt2adevWcc899xAXF6efRwUpyJRTREQE3t7epKenl2lPT08nJibGpKrkzjvv5Ntvv2XJkiU0aNDA7HI81urVq8nIyKBTp06lbXa7nSVLlvD6669js9nw9vY2sULPExsbS6tWrcq0tWzZks8//9ykijzb/fffz0MPPcSoUaMAaNu2Lfv27WPatGkKMhWkOTLl5OfnR+fOnVm4cGFpm8PhYOHChfTo0cPEyjyTYRjceeedfPnllyxatIikpCSzS/Joffv2ZePGjaxbt6700aVLF8aMGcO6desUYkzQs2fPM5Yk2L59OwkJCSZV5Nny8/Px8ir70evt7Y3D4TCpIvenHpkKmDRpEmPHjqVLly5cdNFFvPLKK+Tl5TFu3DizS/M448ePZ9asWXz99dcEBweTlpYGQGhoKAEBASZX53mCg4PPmJ8UFBREeHi45i2ZZOLEiVx88cU8/fTTjBw5khUrVvD222/z9ttvm12aRxo6dCj//Oc/adiwIa1bt2bt2rW89NJL3HzzzWaX5r4MqZDXXnvNaNiwoeHn52dcdNFFxvLly80uySMBZ33MnDnT7NLkpEsvvdSYMGGC2WV4tG+++cZo06aNYbVajRYtWhhvv/222SV5rOzsbGPChAlGw4YNDX9/f6NRo0bGP/7xD8Nms5ldmtvSOjIiIiLitjRHRkRERNyWgoyIiIi4LQUZERERcVsKMiIiIuK2FGRERETEbSnIiIiIiNtSkBERERG3pSAjIiIibktBRkQ8jsVi4auvvjK7DBGpAgoyIlKjbrrpJiwWyxmPgQMHml2aiLghbRopIjVu4MCBzJw5s0yb1Wo1qRoRcWfqkRGRGme1WomJiSnzCAsLA5zDPtOnT2fQoEEEBATQqFEjPvvsszJfv3HjRvr06UNAQADh4eHcdttt5ObmlnnNe++9R+vWrbFarcTGxnLnnXeWOX/kyBGuvvpqAgMDadq0KXPnzq3eb1pEqoWCjIi4nEcffZThw4ezfv16xowZw6hRo9iyZQsAeXl5DBgwgLCwMFauXMmcOXNYsGBBmaAyffp0xo8fz2233cbGjRuZO3cuTZo0KfMeU6dOZeTIkWzYsIHBgwczZswYjh07VqPfp4hUAbO33xYRzzJ27FjD29vbCAoKKvP45z//aRiGYQDG7bffXuZrunXrZvz97383DMMw3n77bSMsLMzIzc0tPf/dd98ZXl5eRlpammEYhhEXF2f84x//OGcNgPHII4+UHufm5hqAMW/evCr7PkWkZmiOjIjUuMsvv5zp06eXaatXr17p8x49epQ516NHD9atWwfAli1baN++PUFBQaXne/bsicPhYNu2bVgsFg4ePEjfvn3PW0O7du1KnwcFBRESEkJGRkZFvyURMYmCjIjUuKCgoDOGeqpKQEDABb3O19e3zLHFYsHhcFRHSSJSjTRHRkRczvLly884btmyJQAtW7Zk/fr15OXllZ5funQpXl5eNG/enODgYBITE1m4cGGN1iwi5lCPjIjUOJvNRlpaWpk2Hx8fIiIiAJgzZw5dunShV69efPzxx6xYsYIZM2YAMGbMGB5//HHGjh3LlClTOHz4MHfddRc33ngj0dHRAEyZMoXbb7+dqKgoBg0aRE5ODkuXLuWuu+6q2W9URKqdgoyI1LgffviB2NjYMm3Nmzdn69atgPOOouTkZO644w5iY2P55JNPaNWqFQCBgYH8+OOPTJgwga5duxIYGMjw4cN56aWXSq81duxYCgoKePnll7nvvvuIiIjg2muvrblvUERqjMUwDMPsIkRESlgsFr788kuGDRtmdiki4gY0R0ZERETcloKMiIiIuC3NkRERl6LRbhEpD/XIiIiIiNtSkBERERG3pSAjIiIibktBRkRERNyWgoyIiIi4LQUZERERcVsKMiIiIuK2FGRERETEbf0/KcJwHskscwkAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "# define the main function\n",
    "def main():\n",
    "    # set the device\n",
    "    device = 'cuda' if torch.cuda.is_available() else 'cpu'\n",
    "\n",
    "    # set the hyperparameters\n",
    "    lr = 0.001\n",
    "    momentum = 0.9\n",
    "    weight_decay = 5e-4\n",
    "    epochs = 10\n",
    "    \n",
    "    # define the model, optimizer and loss function\n",
    "    model = SimpleCNN().to(device)\n",
    "    optimizer = Adam(model.parameters(), lr=lr, weight_decay=weight_decay)\n",
    "    criterion = nn.CrossEntropyLoss()\n",
    "\n",
    "    # Define the learning rate scheduler\n",
    "    lr_scheduler = CosineAnnealingLR(optimizer, T_max=epochs, eta_min=0, last_epoch=-1)\n",
    "\n",
    "    print(f\"Number of model parameters: {sum(p.numel() for p in model.parameters())/1e6:.2f} million\")\n",
    "    print(f\"Running on: {device}\")\n",
    "    # train the model\n",
    "    train_losses = []\n",
    "    test_losses = []\n",
    "    train_accs = []\n",
    "    test_accs = []\n",
    "    for epoch in range(epochs):\n",
    "        start_time = time.time()\n",
    "        train_loss, train_acc = train(model, train_loader, optimizer, criterion,  device)\n",
    "        test_loss, test_acc = test(model, test_loader, criterion, device)\n",
    "        train_losses.append(train_loss)\n",
    "        test_losses.append(test_loss)\n",
    "        train_accs.append(train_acc)\n",
    "        test_accs.append(test_acc)\n",
    "        end_time = time.time()\n",
    "        lr_scheduler.step()\n",
    "\n",
    "        print('Epoch: {}, Train Loss: {:.4f}, Train Acc: {:.2f}%, Test Loss: {:.4f}, Test Acc: {:.2f}%, LR: {:.6f}, Time: {:.2f}s'.format(epoch+1, train_loss, train_acc, test_loss, test_acc, optimizer.param_groups[0]['lr'], end_time-start_time))\n",
    "\n",
    "    # plot the train and test losses\n",
    "    plt.plot(train_losses, label='Train Loss')\n",
    "    plt.plot(test_losses, label='Test Loss')\n",
    "    plt.xlabel('Epoch')\n",
    "    plt.ylabel('Loss')\n",
    "    plt.legend()\n",
    "    plt.show()\n",
    "\n",
    "    # plot the train and test accuracies\n",
    "    plt.plot(train_accs, label='Train Acc')\n",
    "    plt.plot(test_accs, label='Test Acc')\n",
    "    plt.xlabel('Epoch')\n",
    "    plt.ylabel('Accuracy')\n",
    "    plt.legend()\n",
    "    plt.show()\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Hand-crafted implementation of 2D convolution\n",
    "\n",
    "`TensorConv2d` is a hand-crafted implementation of `nn.Conv2d`. This is a test of low-level understanding of convolution. It uses `einops` as a helper library for tensor manipulation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "from einops import rearrange\n",
    "\n",
    "class TensorConv2d(nn.Module):\n",
    "    def __init__(self, n_features, n_filters, kernel_size):\n",
    "        super().__init__()\n",
    "        self.n_features = n_features\n",
    "        self.kernel_size = kernel_size\n",
    "        self.n_filters = n_filters\n",
    "        self.kernel = nn.Parameter(torch.zeros((n_features * kernel_size * kernel_size, n_filters)))\n",
    "        self.bias = nn.Parameter(torch.zeros(n_filters))\n",
    "        self.reset_parameters()\n",
    "\n",
    "    def reset_parameters(self):\n",
    "        nn.init.constant_(self.bias, 0)\n",
    "        nn.init.kaiming_uniform_(self.kernel, a=math.sqrt(5))\n",
    "        \n",
    "    def forward(self, x):\n",
    "        k = self.kernel_size\n",
    "\n",
    "        # make sure that kernel and bias are in the same device as x\n",
    "        if self.kernel.device != x.device:\n",
    "            self.kernel.to(x.device)\n",
    "            self.bias.to(x.device)\n",
    "\n",
    "        # batch, height, width\n",
    "        b = x.shape[0]\n",
    "        h = x.shape[2]\n",
    "        w = x.shape[3]\n",
    "\n",
    "        # making sure the feature map to be convolved is of the right size\n",
    "        # and we dont go past beyond the the feature map boundary\n",
    "        wk = k * (w // k) \n",
    "        hk = k * (h // k)\n",
    "        wf = w % k \n",
    "        hf = h % k\n",
    "\n",
    "        # Tensor Level Convolution\n",
    "        # Basic idea: (Repeat kernel_size times per row and per col) \n",
    "        # 1) convert an image into patches\n",
    "        # 2) perform convolution on each patch which is equivalent to \n",
    "        #  - dot product of each patch with the kernel plus bias term\n",
    "        # 4) move 1 feature point along the horizontal axis (to be done kernel_size times)\n",
    "        # 5) go to 1)\n",
    "        # 6) move 1 feature point along the vertical axis (to be done kernel_size times)\n",
    "        # 7) go to 1)\n",
    "\n",
    "        # Tensor z contains the output of the convolution\n",
    "        # make sure tensor z is the correct device as x\n",
    "        z = torch.empty((b, self.n_filters, h-k+1, w-k+1)).to(x.device)\n",
    "\n",
    "        for i in range(k):\n",
    "            # row offset \n",
    "            # we need to perform offset k times\n",
    "            hoff = i if hf >= i else (-k + i)\n",
    "            for j in range(k):\n",
    "                # column offset \n",
    "                # we need to perform offset k times\n",
    "                woff = j if wf >= j else (-k + j)\n",
    "                \n",
    "                # shift i row and j col\n",
    "                y = x[:, :, i: hk + hoff:, j: wk + woff:]\n",
    "        \n",
    "                # convert to patches (p1 p2 c)\n",
    "                y = rearrange(y, \"b c (h p1) (w p2) -> b h w (p1 p2 c)\", p1=k, p2=k)\n",
    "                # dot product plus bias term\n",
    "                y = y @ self.kernel + self.bias\n",
    "\n",
    "                # sparse feature map: channel first\n",
    "                y = rearrange(y, 'b h w c -> b c h w')\n",
    "\n",
    "                # assign the feature map to the correct position in the output tensor\n",
    "                z[:,:,i::k,j::k] = y\n",
    "        \n",
    "        return z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## PyTorch Lightning (PL) Implementation\n",
    "\n",
    "The following shows how PL simplifies the overall code. We use the same CNN model as above. We also use the same train and test functions. The only difference is that we use the `LightningModule` class to define the model. We also use the `Trainer` class to train the model.\n",
    "\n",
    "\n",
    "PL has built-in support for `accuracy` measurement (`torchmetrics`) and logging (`default`, `tensorboard` and `wandb`). We can also use `torchvision` to download the CIFAR10 dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "import lightning as L\n",
    "import torchmetrics\n",
    "\n",
    "class LSimpleCNN(L.LightningModule):\n",
    "    def __init__(self, n_features=3, kernel_size=3, n_filters=32, num_classes=10, conv2d=nn.Conv2d) -> None:\n",
    "        super().__init__()\n",
    "        self.model = SimpleCNN(n_features, kernel_size, n_filters, num_classes, conv2d)\n",
    "        self.epochs = 10\n",
    "        self.accuracy = torchmetrics.Accuracy(task='multiclass', num_classes=num_classes)\n",
    "        self.train_losses = []\n",
    "        self.train_acc = []\n",
    "        self.val_acc = []\n",
    "        self.val_losses = []\n",
    "        \n",
    "    def forward(self, x):\n",
    "        return self.model(x)\n",
    "        \n",
    "    def training_step(self, batch, batch_idx):\n",
    "        x, y = batch\n",
    "        y_hat = self.model(x)\n",
    "        loss = nn.CrossEntropyLoss()(y_hat, y)\n",
    "        self.train_losses.append(loss)\n",
    "        acc = self.accuracy(y_hat, y)\n",
    "        self.train_acc.append(acc)\n",
    "        self.log(\"train_acc\", acc, on_step=True, on_epoch=True, prog_bar=True, logger=True)\n",
    "        self.log(\"train_loss\", loss, on_step=True, on_epoch=True, prog_bar=True, logger=True)\n",
    "        return { \"loss\": loss, \"acc\": acc }\n",
    "    \n",
    "    def on_train_epoch_start(self):\n",
    "        self.train_losses.clear()\n",
    "    \n",
    "    def on_train_epoch_end(self):\n",
    "        self.log(\"lr\", self.optimizer.param_groups[0]['lr'], on_epoch=True, prog_bar=True, logger=True)\n",
    "\n",
    "    def on_test_epoch_start(self) -> None:\n",
    "        return self.on_validation_epoch_start()\n",
    "\n",
    "    def on_validation_epoch_start(self) -> None:\n",
    "        self.val_acc.clear()\n",
    "        self.val_losses.clear()\n",
    "        \n",
    "    def test_step(self, batch, batch_idx):\n",
    "        return self.validation_step(batch, batch_idx)\n",
    "\n",
    "    def validation_step(self, batch, batch_idx):\n",
    "        x, y = batch\n",
    "        y_hat = self.model(x)\n",
    "        loss = nn.CrossEntropyLoss()(y_hat, y)\n",
    "        acc = self.accuracy(y_hat, y)\n",
    "        self.val_acc.append(acc)\n",
    "        self.val_losses.append(loss)\n",
    "        return {\"test_loss\": loss, \"test_acc\": acc}\n",
    "    \n",
    "    def on_test_epoch_end(self) -> None:\n",
    "        return self.on_validation_epoch_end()\n",
    "    \n",
    "    def on_validation_epoch_end(self):\n",
    "        avg_acc = torch.stack([x for x in self.val_acc]).mean()\n",
    "        avg_loss = torch.stack([x for x in self.val_losses]).mean()\n",
    "        self.log(\"test_loss\", avg_loss, on_epoch=True, prog_bar=True, logger=True)\n",
    "        self.log(\"test_acc\", avg_acc, on_epoch=True, prog_bar=True, logger=True)\n",
    "    \n",
    "    def configure_optimizers(self):\n",
    "        self.optimizer = Adam(self.parameters(), lr=0.001, weight_decay=5e-4)\n",
    "        self.scheduler = CosineAnnealingLR(self.optimizer, T_max=self.epochs, eta_min=0, last_epoch=-1)\n",
    "        \n",
    "        return [self.optimizer], [self.scheduler]\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model creation, training and testing\n",
    "\n",
    "Using PL, model creation, training and testing is simplified. We can also use `Trainer` to log the model metrics to `tensorboard` and `wandb`. For this example, we will just print the results on the screen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "GPU available: True (cuda), used: True\n",
      "TPU available: False, using: 0 TPU cores\n",
      "IPU available: False, using: 0 IPUs\n",
      "HPU available: False, using: 0 HPUs\n",
      "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6,7]\n",
      "\n",
      "  | Name     | Type               | Params\n",
      "------------------------------------------------\n",
      "0 | model    | SimpleCNN          | 113 K \n",
      "1 | accuracy | MulticlassAccuracy | 0     \n",
      "------------------------------------------------\n",
      "113 K     Trainable params\n",
      "0         Non-trainable params\n",
      "113 K     Total params\n",
      "0.455     Total estimated model params size (MB)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sanity Checking: |          | 0/? [00:00<?, ?it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 9: 100%|██████████| 782/782 [00:11<00:00, 66.43it/s, v_num=6, train_acc_step=0.938, train_loss_step=0.313, test_loss=0.717, test_acc=0.753, train_acc_epoch=0.734, train_loss_epoch=0.764, lr=2.45e-5] "
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "`Trainer.fit` stopped: `max_epochs=10` reached.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 9: 100%|██████████| 782/782 [00:11<00:00, 66.34it/s, v_num=6, train_acc_step=0.938, train_loss_step=0.313, test_loss=0.717, test_acc=0.753, train_acc_epoch=0.734, train_loss_epoch=0.764, lr=2.45e-5]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6,7]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Testing DataLoader 0: 100%|██████████| 157/157 [00:01<00:00, 138.89it/s]\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
       "┃<span style=\"font-weight: bold\">        Test metric        </span>┃<span style=\"font-weight: bold\">       DataLoader 0        </span>┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
       "│<span style=\"color: #008080; text-decoration-color: #008080\">         test_acc          </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    0.7527866363525391     </span>│\n",
       "│<span style=\"color: #008080; text-decoration-color: #008080\">         test_loss         </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    0.7170971632003784     </span>│\n",
       "└───────────────────────────┴───────────────────────────┘\n",
       "</pre>\n"
      ],
      "text/plain": [
       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
       "┃\u001b[1m \u001b[0m\u001b[1m       Test metric       \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m      DataLoader 0       \u001b[0m\u001b[1m \u001b[0m┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
       "│\u001b[36m \u001b[0m\u001b[36m        test_acc         \u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   0.7527866363525391    \u001b[0m\u001b[35m \u001b[0m│\n",
       "│\u001b[36m \u001b[0m\u001b[36m        test_loss        \u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   0.7170971632003784    \u001b[0m\u001b[35m \u001b[0m│\n",
       "└───────────────────────────┴───────────────────────────┘\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "[{'test_loss': 0.7170971632003784, 'test_acc': 0.7527866363525391}]"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model = LSimpleCNN()\n",
    "\n",
    "trainer = L.Trainer(max_epochs=10, devices=1, accelerator=\"gpu\",)\n",
    "trainer.fit(model, train_loader, test_loader)\n",
    "trainer.test(model, dataloaders=test_loader)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### PL `LightningDataModule`\n",
    "\n",
    "PL has a built-in `LightningDataModule` class that can be used to create the train and test data loaders. We can also use `torchvision` to download the CIFAR10 dataset.\n",
    "\n",
    "This simplifies the overall management of datasets and dataloaders."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "class LDataModule(L.LightningDataModule):\n",
    "    def __init__(self, batch_size=64) -> None:\n",
    "        super().__init__()\n",
    "        self.batch_size = batch_size\n",
    "        \n",
    "    def prepare_data(self):\n",
    "        # download\n",
    "        torchvision.datasets.CIFAR10(root='~/data', train=True, download=True)\n",
    "        torchvision.datasets.CIFAR10(root='~/data', train=False, download=True)\n",
    "        \n",
    "    def setup(self, stage=None):\n",
    "        # transform\n",
    "        transform_train = torchvision.transforms.Compose([\n",
    "            torchvision.transforms.RandomCrop(32, padding=4),\n",
    "            torchvision.transforms.RandomHorizontalFlip(),\n",
    "            torchvision.transforms.ToTensor(),\n",
    "            torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n",
    "        ])\n",
    "        transform_test = torchvision.transforms.Compose([\n",
    "            torchvision.transforms.ToTensor(),\n",
    "            torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n",
    "        ])\n",
    "        \n",
    "        # split\n",
    "        self.trainset = torchvision.datasets.CIFAR10(root='~/data', train=True, \n",
    "                                                     download=False, \n",
    "                                                     transform=transform_train)\n",
    "        self.testset = torchvision.datasets.CIFAR10(root='~/data', train=False, \n",
    "                                                    download=False, \n",
    "                                                    transform=transform_test)\n",
    "        \n",
    "    def train_dataloader(self):\n",
    "        return torch.utils.data.DataLoader(self.trainset, \n",
    "                                           batch_size=self.batch_size, \n",
    "                                           shuffle=True, num_workers=2)\n",
    "    \n",
    "    def val_dataloader(self):\n",
    "        return torch.utils.data.DataLoader(self.testset, \n",
    "                                           batch_size=self.batch_size, \n",
    "                                           shuffle=False, num_workers=2)\n",
    "    \n",
    "    def test_dataloader(self):\n",
    "        return self.val_dataloader()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Files already downloaded and verified\n",
      "Files already downloaded and verified\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/rowel/anaconda3/envs/speech/lib/python3.10/site-packages/lightning/pytorch/callbacks/model_checkpoint.py:634: Checkpoint directory /home/rowel/github/roatienza/Deep-Learning-Experiments/versions/2023/cnn/demo/lightning_logs/version_6/checkpoints exists and is not empty.\n",
      "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6,7]\n",
      "\n",
      "  | Name     | Type               | Params\n",
      "------------------------------------------------\n",
      "0 | model    | SimpleCNN          | 113 K \n",
      "1 | accuracy | MulticlassAccuracy | 0     \n",
      "------------------------------------------------\n",
      "113 K     Trainable params\n",
      "0         Non-trainable params\n",
      "113 K     Total params\n",
      "0.455     Total estimated model params size (MB)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                                                                           \r"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "`Trainer.fit` stopped: `max_epochs=10` reached.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Files already downloaded and verified\n",
      "Files already downloaded and verified\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1,2,3,4,5,6,7]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Testing DataLoader 0: 100%|██████████| 157/157 [00:01<00:00, 139.74it/s]\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
       "┃<span style=\"font-weight: bold\">        Test metric        </span>┃<span style=\"font-weight: bold\">       DataLoader 0        </span>┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
       "│<span style=\"color: #008080; text-decoration-color: #008080\">         test_acc          </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    0.7527866363525391     </span>│\n",
       "│<span style=\"color: #008080; text-decoration-color: #008080\">         test_loss         </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    0.7170971632003784     </span>│\n",
       "└───────────────────────────┴───────────────────────────┘\n",
       "</pre>\n"
      ],
      "text/plain": [
       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
       "┃\u001b[1m \u001b[0m\u001b[1m       Test metric       \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m      DataLoader 0       \u001b[0m\u001b[1m \u001b[0m┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
       "│\u001b[36m \u001b[0m\u001b[36m        test_acc         \u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   0.7527866363525391    \u001b[0m\u001b[35m \u001b[0m│\n",
       "│\u001b[36m \u001b[0m\u001b[36m        test_loss        \u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   0.7170971632003784    \u001b[0m\u001b[35m \u001b[0m│\n",
       "└───────────────────────────┴───────────────────────────┘\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "[{'test_loss': 0.7170971632003784, 'test_acc': 0.7527866363525391}]"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "loader = LDataModule()\n",
    "trainer.fit(model, loader)\n",
    "trainer.test(model, dataloaders=loader)"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "52772734322c44a04e342c358be70f2ff4d97da358cb3cd38ceb0f6066598be5"
  },
  "kernelspec": {
   "display_name": "Python 3.7.3 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
